﻿using System;
using System.Diagnostics.Contracts;

namespace Isuka.Algorithms
{
    /// <summary>
    /// 数値計算用
    /// </summary>
    public static class Calculation
    {
        /// <summary>
        /// 二分法で方程式 f(x) == 0 を解く
        /// </summary>
        /// <param name="f">関数 f(x)</param>
        /// <param name="left">区間の左端</param>
        /// <param name="right">区間の右端</param>
        /// <param name="convergence">収束条件。既定値は 1.0e-5</param>
        /// <returns>解。解がなければ NaN を返す</returns>
        public static double Bisection(Func<double, double> f, double left, double right, double convergence = 1.0e-5)
        {
            Contract.Requires<ArgumentNullException>(f != null, "f");
            Contract.Requires<ArgumentException>(left < right);
            Contract.Requires<ArgumentException>(0 < convergence && convergence < 1);

            var a = left;
            var b = right;
            var fa = f(a);
            var fb = f(b);
            var r = double.NaN;
            if (fa * fb <= 0)                   //  f(a) * f(b) > 0 でない
            {
                do
                {
                    r = (a + b) / 2.0;
                    var fr = f(r);
                    if (fr == 0)                //  f(r) == 0 なら、r が 解
                    {
                        break;
                    }
                    else if (fa * fr <= 0)
                    {
                        b = r;
                        fb = fr;
                    }
                    else
                    {
                        a = r;
                        fa = fr;
                    }
                } while (b - a >= convergence); //  収束条件になるまでループ
            }
            return r;
        }
    }
}
